#include "jni.h"

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <dlfcn.h>

#include <GLES3/gl3.h>
#include <multimedia/image/image_pixel_map.h>

namespace ohos {
    static void doThrowIAE(JNIEnv* env, const char* msg) {
        env->ThrowNew(env->FindClass("java/lang/IllegalArgumentException"), msg);
    }
    // The internal format is no longer the same as pixel format, per Table 2 in
    // https://www.khronos.org/registry/OpenGL-Refpages/es3.1/html/glTexImage2D.xhtml
    static int getPixelFormatFromInternalFormat(uint32_t internalFormat) {
        switch (internalFormat) {
            // For sized internal format.
            case GL_RGBA16F:
            return GL_RGBA;
            // Base internal formats and pixel formats are still the same, see Table 1 in
            // https://www.khronos.org/registry/OpenGL-Refpages/es3.1/html/glTexImage2D.xhtml
            default:
            return internalFormat;
        }
    }

    static jint util_texImage2D(JNIEnv * env, jclass clazz,
                                jint target, jint level, jint internalformat,
                                jobject jbitmap, jint type, jint border)
    {
        OhosPixelMapInfo pixelMapInfo;
        GetImageInfo(env, jbitmap, pixelMapInfo);
        const int w = pixelMapInfo.width;
        const int h = pixelMapInfo.height;
        void* p = nullptr;
        AccessPixels(env, jbitmap, &p);
        glTexImage2D(target, level, internalformat, w, h, border,
                getPixelFormatFromInternalFormat(internalformat), type, p);
        UnAccessPixels(env, jbitmap);
        return 0;
    }

    extern "C" JNIEXPORT void JNICALL
    Java_ohos_opengl_GLUtils_texImage2D(JNIEnv * env, jclass clazz,
    jint target, jint level, jint internalformat,
    jobject jbitmap, jint type, jint border) {
    util_texImage2D(env, clazz, target, level, internalformat,jbitmap, type, border);
}
/*
 * ETC1 methods.
 */

static jclass nioAccessClass;
static jclass bufferClass;
static jmethodID getBasePointerID;
static jmethodID getBaseArrayID;
static jmethodID getBaseArrayOffsetID;
static jfieldID positionID;
static jfieldID limitID;
static jfieldID elementSizeShiftID;

/* Cache method IDs each time the class is loaded. */

static void *
getPointer(JNIEnv * _env, jobject buffer, jint * remaining)
{
    jint position;
    jint limit;
    jint elementSizeShift;
    jlong pointer;

    position = _env->GetIntField(buffer, positionID);
    limit = _env->GetIntField(buffer, limitID);
    elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID);
    *remaining = (limit - position) << elementSizeShift;
    pointer = _env->CallStaticLongMethod(nioAccessClass,
            getBasePointerID, buffer);
    if (pointer != 0L) {
        return reinterpret_cast <
        void *>(pointer);
    }
    return NULL;
}

class BufferHelper {
public:

    BufferHelper(JNIEnv * env, jobject buffer) {
        mEnv = env;
        mBuffer = buffer;
        mData = NULL;
        mRemaining = 0;
    }

    bool checkPointer(const char* errorMessage) {
        if (mBuffer) {
            mData = getPointer(mEnv, mBuffer, &mRemaining);
            if (mData == NULL) {
                doThrowIAE(mEnv, errorMessage);
            }
            return mData != NULL;
        } else {
            doThrowIAE(mEnv, errorMessage);
            return false;
        }
    }

    inline void* getData() {
        return mData;
    }

    inline jint remaining() {
        return mRemaining;
    }

private:
    JNIEnv* mEnv;
    jobject mBuffer;
    void* mData;
    jint mRemaining;
};


typedef struct _ClassRegistrationInfo {
    const char* classPath;
    const JNINativeMethod* methods;
    size_t methodCount;
} ClassRegistrationInfo;

} // namespace ohos
